package zisu.netty.practice.netty.codec;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;

/**
 * @Desc: ""
 * @Author: caixiang
 * @DATE: 2020/7/8 8:53
 */
public class NettyServer {
    public static void main(String[] args) throws InterruptedException {
        /**
         * 创建bossGroup 和 workerGroup
         * 说明：
         *      1.创建两个线程组bossGroup 和 workerGroup
         *      2.bossGroup只是处理连接请求，，真正的和客户端处理会交给workerGroup
         *      3.两个都是无限循环
         *      4.bossGroup 和 workerGroup含有的子线程（NioEventLoop）的个数 默认是CPU 核数*2
         * */
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //创建服务器端的启动参数，配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();

            //使用链式进行设置
            bootstrap.group(bossGroup,workerGroup)   //设置两个线程组
                    .channel(NioServerSocketChannel.class)     //使用NioServerSocketChannel作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG,128)   //设置线程队列得到连接个数
                    .childOption(ChannelOption.SO_KEEPALIVE,true)  //设置保持活动连接的状态
                    .handler(new NettyServerHandler())    //该handler 对应的是boss handler
                    .childHandler(new ChannelInitializer<SocketChannel>() {   //创建一个通道初始化对象（匿名方法），，对应的是workGroup
                        //给pipline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {  //childHandler() 用来设置业务处理类（这里是ChannelInitializer这个类）
                            //不同的客户过来有不同的channel
                            System.err.println("客户 socketChannel HashCode:"+ch.hashCode());
                            //可以使用一个集合管理所有的channel。 在推送消息时候，可以将业务加入到各个channel 对应的 NIOEventLoop
                            // 的 taskQueue 或者 schedualTaskQueue 中去
                            ChannelPipeline channelPipeline = ch.pipeline();
                            //在pipline 中加入 ProtoBufDecoder,,要指定你的解码对象 ( 因为我们是用protobuf来传输的 )
                            channelPipeline.addLast("decoder",new ProtobufDecoder(StudentPOJO.Student.getDefaultInstance()));
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    }); //给我们的workerGroup 的 EventLoop 对应的管道 设置处理器

            System.out.println("服务器 is ready..............");

            //绑定一个端口并且同步 ，生成一个ChannelFuture 对象。
            //启动服务器（并绑定端口）
            ChannelFuture cf = bootstrap.bind(6668).sync();

            //可以对 server 的ChannelFuture 添加事件监听
            //cf.addListener()

            //对关闭通道进行监听（只有当 关闭这个通道 Event 来的时候才会来处理）,,不是一启动 就马上关闭
            cf.channel().closeFuture().sync();

        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
